#!/usr/bin/env bash

red="\033[0;31m"
green="\033[0;32m"
cyan="\033[0;36m"
bold="\033[1m"
nocolor="\033[0m"

#-------------------------------------------------------------------------------
function step() {
   box="   "
   color="$nocolor"
   if [ "$version" != "<x.y.z>" ]
   then
      if [ -e "/tmp/.step-$1-$version" ]
      then
         color="$green"
         box="[x]"
      else
         color="$bold"
         box="[ ]"
      fi
   fi
   echo -e "$color $box Step $c) $2"
   echo "        $0 $version $1 $3"
   echo -e "$nocolor"
   c="$[c+1]"
}

#-------------------------------------------------------------------------------
function usage() {
   echo "Make a Kong patch release using this script:"
   echo ""
   echo "Usage:"
   if [ "$version" = "<x.y.z>" ]
   then
      echo "     List executed steps for a given release"
      echo "        $0 $version $1 $3"
      echo
   fi
   c=1
   step "create"           "create the branch"
   step "write_changelog"  "prepare the changelog"
   step "commit_changelog" "commit the changelog"
   step "version_bump"     "bump and commit the version number"
   step "submit"           "push and submit a release PR"
   step "docs_pr"          "push and submit a docs.konghq.com PR for the release"
   step "merge"            "merge, tag and sign the release"
   step "update_docker"    "update and submit a PR to Kong's docker-kong repo"
   step "merge_docker"     "merge, tag and sign Kong's docker-kong PR"
   step "submit_docker"    "submit a PR to docker-library/official-images"
   step "homebrew"         "bump version and submit a PR to homebrew-kong"
   step "luarocks"         "upload to LuaRocks" "<api-key>"
   step "vagrant"          "bump version and submit a PR to kong-vagrant"
   step "pongo"            "bump version and submit a PR to kong-pongo"
   exit 0
}

#-------------------------------------------------------------------------------
function die() {
   echo
   echo -e "$red$bold*** $@$nocolor"
   echo "See also: $0 --help"
   echo
   exit 1
}

#-------------------------------------------------------------------------------
function SUCCESS() {
   echo
   echo -e "$green$bold****************************************$nocolor$bold"
   for line in "$@"
   do
      echo "$line"
   done
   echo -e "$green$bold****************************************$nocolor"
   echo
   touch /tmp/.step-$step-$version
   exit 0
}

#-------------------------------------------------------------------------------
function CONFIRM() {
   echo
   echo -e "$cyan$bold----------------------------------------$nocolor$bold"
   for line in "$@"
   do
      echo "$line"
   done
   echo -e "$cyan$bold----------------------------------------$nocolor"
   read
}

#-------------------------------------------------------------------------------
# Dependency checks
#-------------------------------------------------------------------------------

hub --version &> /dev/null || die "hub is not in PATH. Get it from https://github.com/github/hub"

if resty -v &> /dev/null
then
   LUA=resty
elif lua -v &> /dev/null
then
   LUA=lua
else
   die "Lua interpreter is not in PATH. Install any Lua or OpenResty to run this script."
fi

#-------------------------------------------------------------------------------
# Default help
#-------------------------------------------------------------------------------

if [ "$1" = "-h" ] || [ "$1" = "--help" ] || ! [ "$1" ]
then
   version="<x.y.z>"
   usage
fi

#-------------------------------------------------------------------------------
# Variables
#-------------------------------------------------------------------------------

version="$1"
step="$2"

major=${version%%.*}
rest=${version#*.}
minor=${rest%%.*}
patch=${rest##*.}
rockspec="kong-$version-0.rockspec"
branch="release/$version"

if ! [[ "$version" =~ ^[0-9]+.[0-9]+.[0-9]$ ]]
then
   die "first argument must be a version in x.y.z format"
fi

if [ "$step" = "" ]
then
   usage
fi

EDITOR="${EDITOR-$VISUAL}"

#-------------------------------------------------------------------------------
function prepare_changelog() {
   $LUA -e '
      local fd_in = io.open("CHANGELOG.md", "r")
      local fd_out = io.open("CHANGELOG.md.new", "w")
      local version = "'$version'"

      local state = "start"
      for line in fd_in:lines() do
         if state == "start" then
            if line:match("^%- %[") then
               fd_out:write("- [" .. version .. "](#" .. version:gsub("%.", "") .. ")\n")
               state = "toc"
            end
         elseif state == "toc" then
            if not line:match("^%- %[") then
               state = "start_log"
            end
         elseif state == "start_log" then
            fd_out:write("\n")
            fd_out:write("## [" .. version .. "]\n")
            fd_out:write("\n")
            local today = os.date("*t")
            fd_out:write(("> Released %04d/%02d/%02d\n"):format(today.year, today.month, today.day))
            fd_out:write("\n")
            fd_out:write("<<< TODO Introduction, plus any sections below >>>\n")
            fd_out:write("\n")
            fd_out:write("### Fixes\n")
            fd_out:write("\n")
            fd_out:write("##### Core\n")
            fd_out:write("\n")
            fd_out:write("##### CLI\n")
            fd_out:write("\n")
            fd_out:write("##### Configuration\n")
            fd_out:write("\n")
            fd_out:write("##### Admin API\n")
            fd_out:write("\n")
            fd_out:write("##### PDK\n")
            fd_out:write("\n")
            fd_out:write("##### Plugins\n")
            fd_out:write("\n")
            fd_out:write("\n")
            fd_out:write("[Back to TOC](#table-of-contents)\n")
            fd_out:write("\n")
            state = "log"
         elseif state == "log" then
            local prev_version = line:match("^%[(%d+%.%d+%.%d+)%]: ")
            if prev_version then
               fd_out:write("[" .. version .. "]: https://github.com/Kong/kong/compare/" .. prev_version .."..." .. version .. "\n")
               state = "last"
            end
         end

         fd_out:write(line .. "\n")
      end
      fd_in:close()
      fd_out:close()
   '
   mv CHANGELOG.md.new CHANGELOG.md
}

#-------------------------------------------------------------------------------
function bump_docs_kong_versions() {
   $LUA -e '
      local fd_in = io.open("app/_data/kong_versions.yml", "r")
      local fd_out = io.open("app/_data/kong_versions.yml.new", "w")
      local version = "'$version'"

      local state = "start"
      for line in fd_in:lines() do
         if state == "start" then
            if line:match("^  release: \"'$major'.'$minor'.x\"") then
               state = "version"
            end
            fd_out:write(line .. "\n")
         elseif state == "version" then
            if line:match("^  version: \"") then
               fd_out:write("  version: \"'$version'\"\n")
               state = "wait_for_luarocks_version"
            else
               fd_out:write(line .. "\n")
            end
         elseif state == "wait_for_luarocks_version" then
            if line:match("^  luarocks_version: \"") then
               fd_out:write("  luarocks_version: \"'$version'-0\"\n")
               state = "last"
            else
               fd_out:write(line .. "\n")
            end
         elseif state == "last" then
            fd_out:write(line .. "\n")
         end
      end
      fd_in:close()
      fd_out:close()
   '
   mv app/_data/kong_versions.yml.new app/_data/kong_versions.yml
}

#-------------------------------------------------------------------------------
function make_github_release_file() {
   versionlink=$(echo $version | tr -d .)
   cat <<EOF > release-$version.txt
$version

**Download Kong $version and run it now:**

- https://konghq.com/install/
- [Docker Image](https://hub.docker.com/_/kong/)

Links:
- [$version Changelog](https://github.com/Kong/kong/blob/master/CHANGELOG.md#$versionlink)
EOF
}

#-------------------------------------------------------------------------------
function bump_homebrew() {
   curl -L -o "kong-$version.tar.gz" "https://bintray.com/kong/kong-src/download_file?file_path=kong-$version.tar.gz"
   sum=$(sha256sum "kong-$version.tar.gz" | awk '{print $1}')
   sed -i 's/kong-[0-9.]*.tar.gz/kong-'$version'.tar.gz/' Formula/kong.rb
   sed -i 's/sha256 ".*"/sha256 "'$sum'"/' Formula/kong.rb
}

#-------------------------------------------------------------------------------
function bump_vagrant() {
   sed -i 's/version = "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*"/version = "'$version'"/' Vagrantfile
   sed -i 's/`[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*`/`'$version'`/' README.md
}

#-------------------------------------------------------------------------------
function ensure_recent_luarocks() {
   if ! ( luarocks upload --help | grep -q temp-key )
   then
      if [ `uname -s` = "Linux" ]
      then
         set -e
         source .requirements
         lv=3.2.1
         pushd /tmp
         rm -rf luarocks-$lv
         mkdir -p luarocks-$lv
         cd luarocks-$lv
         curl -L -o "luarocks-$lv-linux-x86_64.zip" https://luarocks.github.io/luarocks/releases/luarocks-$lv-linux-x86_64.zip
         unzip luarocks-$lv-linux-x86_64.zip
         export PATH=/tmp/luarocks-$lv/luarocks-$lv-linux-x86_64:$PATH
         popd
      else
         die "Your LuaRocks version is too old. Please upgrade LuaRocks."
      fi
   fi
}

case "$step" in
   #---------------------------------------------------------------------------
   create)
      if [ $(git status --untracked-files=no --porcelain | wc -l) != "0" ]
      then
         die "Local tree is not clean, please commit or stash before running this."
      fi

      set -e
      git checkout master
      git pull
      git checkout -B "$branch"

      SUCCESS "Release branch was created locally." \
              "You are ready to run the next step:" \
              "    $0 $version write_changelog"
      ;;
   #---------------------------------------------------------------------------
   write_changelog)
      if ! grep -q "\[$version\]" CHANGELOG.md
      then
         prepare_changelog
      fi

      CONFIRM "Press Enter to open your text editor ($EDITOR) to edit CHANGELOG.md" \
              "or Ctrl-C to cancel."

      $EDITOR CHANGELOG.md

      SUCCESS "If you need to further edit the changelog," \
              "you can run this step again."
              "If it is ready, you can proceed to the next step" \
              "which will commit it:" \
              "    $0 $version commit_changelog"
      ;;
   #---------------------------------------------------------------------------
   commit_changelog)
      if ! git status CHANGELOG.md | grep -q "modified:"
      then
         die "No changes in CHANGELOG.md to commit. Did you write the changelog?"
      fi

      git diff

      CONFIRM "If everything looks all right, press Enter to commit" \
              "or Ctrl-C to cancel."

      set -e
      git add CHANGELOG.md
      git commit -m "docs(changelog) add $version changes"
      git log -n 1

      SUCCESS "The changelog is now committed locally." \
              "You are ready to run the next step:" \
              "    $0 $version version_bump"
      ;;
   #---------------------------------------------------------------------------
   version_bump)
      if ! grep -q "patch = $patch" kong/meta.lua
      then
         sed -i 's/patch = [0-9]*/patch = '$patch'/' kong/meta.lua
         git add kong/meta.lua
      fi
      if ! [ -f "$rockspec" ]
      then
         git mv kong-*-0.rockspec "$rockspec"
         sed -i 's/^version = ".*"/version = "'$version'-0"/' "$rockspec"
         sed -i 's/^  tag = ".*"/  tag = "'$version'"/' "$rockspec"
      fi

      git status
      git diff

      CONFIRM "If everything looks all right, press Enter to make the release commit" \
              "or Ctrl-C to cancel."

      git add $rockspec

      git commit -m "release: $version"
      git log -n 1

      SUCCESS "Version bump for the release is now committed locally." \
              "You are ready to run the next step:" \
              "    $0 $version submit"
      ;;
   #---------------------------------------------------------------------------
   submit)
      if ! git log -n 1 | grep -q "release: $version"
      then
         die "Release commit is not at the top of the current branch. Did you commit the version bump?"
      fi

      git log

      CONFIRM "Press Enter to push the branch and open the release PR" \
              "or Ctrl-C to cancel."

      set -e
      git push --set-upstream origin "$branch"
      hub pull-request -b master -h "$branch" -m "Release: $version" -l "pr/please review,pr/do not merge"

      SUCCESS "Now get the above PR reviewed and approved." \
              "Once it is approved, you can continue to the 'merge' step." \
              "In the mean time, you can run the 'docs_pr' step:" \
              "    $0 $version docs_pr"
      ;;
   #---------------------------------------------------------------------------
   docs_pr)
      if [ -d ../docs.konghq.com ]
      then
         cd ../docs.konghq.com
      else
         cd ..
         git clone https://github.com/kong/docs.konghq.com
         cd docs.konghq.com
      fi
      git checkout master
      git pull
      git checkout -B "$branch"
      bump_docs_kong_versions

      git diff

      CONFIRM "If everything looks all right, press Enter to commit and send a PR to https://github.com/kong/docs.konghq.com" \
              "or Ctrl-C to cancel."

      set -e
      git add app/_data/kong_versions.yml
      git commit -m "chore(*) update release metadata for $version"

      git push --set-upstream origin "$branch"
      hub pull-request -b master -h "$branch" -m "Release: $version" -l "pr/please review,pr/do not merge"

      SUCCESS "Make sure you give Team Docs a heads-up" \
              "once the release is pushed to the main repo." \
              "When the main release PR is approved, you can proceed to:" \
              "    $0 $version merge"
      ;;
   #---------------------------------------------------------------------------
   merge)
      CONFIRM "Press Enter to merge the PR into master and push the tag and Github release" \
              "or Ctrl-C to cancel."

      set -e
      git checkout "$branch"
      git pull
      git checkout master
      git pull
      git merge "$branch"
      git push
      git tag -s "$version" -m "$version"
      git push origin "$version"

      make_github_release_file

      hub release create -F release-$version.txt "$version"
      rm -f release-$version.txt

      SUCCESS "Make sure the packages are built and available on Bintray" \
              "before continuing to the following steps." \
              "Once they are built, you may run the following steps in parallel:" \
              "* 'homebrew'" \
              "* 'luarocks'" \
              "* 'vagrant'" \
              "* 'update_docker', then 'merge_docker', then 'submit_docker'"
      ;;
   #---------------------------------------------------------------------------
   update_docker)
      if [ -d ../docker-kong ]
      then
         cd ../docker-kong
      else
         cd ..
         git clone https://github.com/kong/docker-kong
         cd docker-kong
      fi

      set -e
      ./update.sh "$version"

      SUCCESS "Make sure you get the PR above approved and merged" \
              "before continuing to the step 'merge_docker'."
      ;;
   #---------------------------------------------------------------------------
   merge_docker)
      if [ -d ../docker-kong ]
      then
         cd ../docker-kong
      else
         cd ..
         git clone https://github.com/kong/docker-kong
         cd docker-kong
      fi

      set -e
      git checkout "$branch"
      git pull
      git checkout master
      git pull
      git merge "$branch"
      git push
      git tag -s "$version" -m "$version"
      git push origin "$version"

      SUCCESS "Now you can run the next step:" \
              "    $0 $version submit_docker"
      ;;
   #---------------------------------------------------------------------------
   submit_docker)
      if [ -d ../docker-kong ]
      then
         cd ../docker-kong
      else
         cd ..
         git clone https://github.com/kong/docker-kong
         cd docker-kong
      fi

      set -e
      ./submit.sh -p "$version"

      SUCCESS "Once this is approved in the main repo," \
              "run the procedure for generating the RedHat container."
      ;;
   #---------------------------------------------------------------------------
   homebrew)
      if [ -d ../homebrew-kong ]
      then
         cd ../homebrew-kong
      else
         cd ..
         git clone https://github.com/kong/homebrew-kong
         cd homebrew-kong
      fi

      git checkout master
      git pull
      git checkout -B "$branch"
      bump_homebrew

      git diff

      CONFIRM "If everything looks all right, press Enter to commit and send a PR to https://github.com/kong/homebrew-kong" \
              "or Ctrl-C to cancel."

      set -e
      git add Formula/kong.rb
      git commit -m "chore(kong) bump kong to $version"

      git push --set-upstream origin "$branch"
      hub pull-request -b master -h "$branch" -m "Release: $version"

      SUCCESS "Make sure you get the PR above approved and merged."
      ;;
   #---------------------------------------------------------------------------
   pongo)
      if [ -d ../kong-pongo ]
      then
         cd ../kong-pongo
      else
         cd ..
         git clone https://github.com/kong/kong-pongo
         cd kong-pongo
      fi

      git checkout master
      git pull
      ./assets/add_version.sh CE $version
      SUCCESS "Make sure you get the PR above approved and merged."
      ;;
   #---------------------------------------------------------------------------
   vagrant)
      if [ -d ../kong-vagrant ]
      then
         cd ../kong-vagrant
      else
         cd ..
         git clone https://github.com/kong/kong-vagrant
         cd kong-vagrant
      fi

      git checkout master
      git pull
      git checkout -B "$branch"
      bump_vagrant

      git diff

      CONFIRM "If everything looks all right, press Enter to commit and send a PR to https://github.com/kong/kong-vagrant" \
              "or Ctrl-C to cancel."

      set -e
      git add README.md Vagrantfile
      git commit -m "chore(*) bump Kong to $version"

      git push --set-upstream origin "$branch"
      hub pull-request -b master -h "$branch" -m "Release: $version"

      SUCCESS "Make sure you get the PR above approved and merged."
      ;;
   #---------------------------------------------------------------------------
   luarocks)
      if ! [ "$3" ]
      then
         die "Kong API key for LuaRocks is required as an argument."
      fi

      set -e
      ensure_recent_luarocks

      luarocks --version

      luarocks upload --temp-key="$3" "$rockspec" --force

      SUCCESS "The LuaRocks entry is now up!"
      ;;
   #---------------------------------------------------------------------------
   *)
      die "Unknown step!"
      ;;
esac
